/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __VECTOR_H__
#define __VECTOR_H__

#include <math.h>
#include <Converter.h>
#include <algorithm>

namespace liba
{
namespace math
{

//generic vector implementation
template<typename T, int dim>
class Vector
{
	T buf[dim];
public:

	typedef Vector<T, dim> this_type;

	Vector()
	{
		for( size_t i=0; i < dim; ++i )
		{
			data( i ) = T();
		}
	}

	template< typename U >
	Vector( const Vector<U, dim> & v )
	{
		for( size_t i=0; i < dim; ++i )
		{
			data( i ) = T( v.data( i ) );
		}
	}

	const T * data()const
	{
		return buf;
	}

	T * data()
	{
		return buf;
	}
	
	const T & data( size_t i )const
	{
		return buf[i];
	}
	
	T & data( size_t i )
	{
		return buf[i];
	}
	
	T & operator[] ( size_t i )
	{
		return data(i);
	}
	
	const T & operator[] ( size_t i )const
	{
		return data(i);
	}

	this_type operator- ()const
	{
		this_type temp_vector;
		for( size_t i=0; i < dim; ++i )
		{
			temp_vector.data( i ) = -data(i);
		}
		return temp_vector;
	}
	
	bool operator== ( const this_type & vec )const
	{
		for( size_t i=0; i < dim; ++i )
		{
			if( data(i) != vec.data(i) )
				return false;
		}
		return true;
	}

	bool operator!= ( const this_type & vec )const
	{
		return !operator==( vec );
	}

	const this_type & operator+= ( const this_type & vec )
	{
		for( int i=0; i< dim; ++i )
		{
			data(i) += vec.data(i);
		}
		return *this;
	}
	
	const this_type & operator-= ( const this_type & vec )
	{
		for( size_t i = 0; i < dim; ++i )
		{
			data(i) -= vec.data(i);
		}
		return *this;
	}

	const this_type & operator*= ( const T & f )
	{
		for( size_t i = 0; i < dim; ++i )
		{
			data(i) *= f;
		}
		return *this;
	}

	const this_type & operator/= ( const T & f )
	{
		for( size_t i = 0; i < dim; ++i )
		{
			data(i) /= f;
		}
		return *this;
	}

	static T dot( const this_type & u, const this_type & v )
	{
		T res( 0 );
		for( size_t i=0; i < dim; ++i )
		{
			res += u.data(i) *  v.data(i);
		}
		return res;
	}

	T length_2()const
	{
		return dot( *this, *this );
	}

	T length_2( const this_type & vec )const
	{
		this_type temp_vec = vec - *this;
		return dot( temp_vec, temp_vec );
	}

	T length()const
	{
		return T( sqrt( length_2() ) );
	}

	T length( const this_type & vec )const
	{
		return T( sqrt( length_2(vec) ) );
	}

	void normalize()
	{
		T l = length();
		if( l != T(0) ) 
		{
			*this /= l;
		}
	}

	static this_type normalize(const this_type & v)
	{
		this_type n(v);
		n.normalize();
		return n;
	}

	static this_type zero()
	{
		this_type vec;
		std::fill( vec.buf, vec.buf + dim, T(0) );
		return vec;
	}

	bool go_to( const this_type & future, const T & sp )
	{
		this_type delta = future - *this;
		T len = delta.length();
		if( len <= sp )
		{
			*this = future;
			return true;
		}
		delta.normalize();
		*this += delta * sp;
		return false;
	}

	template<typename Sym>
	struct ConverterBuffer
		:	public converter::ConverterStaticBuffer<Sym, 28*dim>
	{
		typedef converter::ConverterStaticBuffer<Sym, 28*dim> StaticBuffer;
		bool convert(const this_type & value)
		{
			StaticBuffer::size = converter::simple_print( StaticBuffer::data, value[0] );
			for( size_t i = 1; i < dim; ++i )
			{
				StaticBuffer::data[StaticBuffer::size++] = ' ';
				StaticBuffer::size += converter::simple_print( StaticBuffer::data + StaticBuffer::size, value[i] );
			}
			return true;
		}
	};

	template<class Sym>
	bool convert(const Sym * be, const Sym * en)
	{
		const Sym * beg = be;
		for( size_t i = 0; i < dim; ++i )
		{
			beg = converter::simple_convert( beg, en, &operator[](i));
			if( !beg )
				return false;
		}
		return true;
	}
};

template<typename T, int dim>
inline Vector<T, dim> operator + (const Vector<T,dim> & u,const Vector<T,dim> & v )
{
	return Vector<T,dim>( u ) += v;
}

template<typename T, int dim>
inline Vector<T, dim> operator - (const Vector<T,dim> & u,const Vector<T,dim> & v )
{
	return Vector<T,dim>( u ) -= v;
}

template<typename T, int dim>
inline Vector<T, dim> operator * (const Vector<T,dim> & u, const T & f )
{
	return Vector<T,dim>( u ) *= f;
}

template<typename T, int dim>
inline Vector<T, dim> operator * (const T & f, const Vector<T,dim> & u )
{
	return Vector<T,dim>( u ) *= f;
}

template<typename T, int dim>
inline Vector<T, dim> operator / ( const Vector<T,dim> & u, const T & f )
{
	return Vector<T,dim>( u ) /= f;
}

template<int dim>
inline Vector<float, dim> operator * (const Vector<float,dim> & u, const double & f )
{
	return Vector<float,dim>( u ) *= float(f);
}

template<int dim>
inline Vector<float, dim> operator / ( const Vector<float,dim> & u, const double & f )
{
	return Vector<float,dim>( u ) /= (float)f;
}

template<typename T, int dim>
inline T norma( Vector<T, dim> a, Vector<T, dim> b )
{
	return a.length( b );
}

inline double norma( double a, double b )
{
	return fabs(b - a);
}

} //namespace math
} //namespace liba
using namespace liba;

#endif //__VECTOR_H__